package com.leyou.search.service;

import com.leyou.client.ItemClient;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.vo.PageResult;
import com.leyou.item.dto.BrandDTO;
import com.leyou.item.dto.CategoryDTO;
import com.leyou.item.dto.SpecParamDTO;
import com.leyou.search.dto.GoodsDTO;
import com.leyou.search.dto.SearchRequest;
import com.leyou.search.pojo.Goods;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchService {
    @Autowired
    private ElasticsearchTemplate esTemplate;

    @Autowired
    private ItemClient itemClient;

    public PageResult<GoodsDTO> pageQuery(SearchRequest searchRequest) {
        String key = searchRequest.getKey();
        if(StringUtils.isBlank(key)){
            throw  new LyException(ExceptionEnum.INVALID_PARAM_ERROR);
        }
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","subTitle","skus"},null));

        queryBuilder.withQuery(QueryBuilders.matchQuery("all",key).operator(Operator.AND));
        queryBuilder.withPageable(PageRequest.of(searchRequest.getPage()-1,searchRequest.getSize()));

        AggregatedPage<Goods> result = esTemplate.queryForPage(queryBuilder.build(), Goods.class);

        List<Goods> list = result.getContent();
        long total = result.getTotalElements();
        int totalPages = result.getTotalPages();

        return new PageResult<>(total,totalPages, BeanHelper.copyWithCollection(list,GoodsDTO.class));
    }

    public Map<String, List<?>> queryFilters(SearchRequest searchRequest) {

        Map<String, List<?>> filterMap = new HashMap<>();

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withPageable(PageRequest.of(0,1));
        queryBuilder.withSourceFilter(new FetchSourceFilterBuilder().build());
        queryBuilder.withQuery(buildBasicQuery(searchRequest));

        String categoryAgg="categoryAgg";
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAgg).field("categoryId"));
        String brandAgg="brandAgg";
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAgg).field("brandId"));

        AggregatedPage<Goods> goodsAggregatedPage = this.esTemplate.queryForPage(queryBuilder.build(), Goods.class);
        Aggregations aggregations = goodsAggregatedPage.getAggregations();

        LongTerms categoryTerms = aggregations.get(categoryAgg);

        List<Long> cids = new ArrayList<>();
        categoryTerms.getBuckets().forEach(bucket -> {
            cids.add(bucket.getKeyAsNumber().longValue());
        });

        LongTerms brandTerms = aggregations.get(brandAgg);

        List<Long> brandIds = brandTerms.getBuckets().stream()
                .map(LongTerms.Bucket::getKeyAsNumber)
                .map(Number::longValue)
                .collect(Collectors.toList());

        List<BrandDTO> brandList=itemClient.queryBrandByIds(brandIds);
        filterMap.put("品牌",brandList);
        List<CategoryDTO> categoryList=itemClient.queryCategoryByIds(cids);
        filterMap.put("分类",categoryList);


        if(categoryList!=null&&categoryList.size()==1){
            handleSpecAgg(cids.get(0),buildBasicQuery(searchRequest),filterMap);
        }

        return filterMap;
    }

    private QueryBuilder buildBasicQuery(SearchRequest request) {
        // 构建基本的match查询
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        queryBuilder.must(QueryBuilders.matchQuery("all",request.getKey()).operator(Operator.AND));
        for(Map.Entry<String,String> entry:request.getFilter().entrySet()){
            String key = entry.getKey();
            if ("分类".equals(key)) {
                key = "categoryId";
            } else if ("品牌".equals(key)) {
                key = "brandId";
            } else {
                key = "specs." + key + ".keyword";
            }
            queryBuilder.filter(QueryBuilders.termQuery(key, entry.getValue()));
        }
        return queryBuilder;
    }

    private void handleSpecAgg(Long cid, QueryBuilder basicQuery, Map<String,List<?>> filterMap){

        List<SpecParamDTO> specParamDTOS = itemClient.querySpecParams(null, cid, true);

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(basicQuery);
        queryBuilder.withPageable(PageRequest.of(0,1));
        queryBuilder.withSourceFilter(new FetchSourceFilterBuilder().build());

        for(SpecParamDTO specParamDTO:specParamDTOS){
            String name = specParamDTO.getName();
            queryBuilder.addAggregation(AggregationBuilders.terms(name).field("specs."+name+".keyword"));
        }

        AggregatedPage<Goods> resut = esTemplate.queryForPage(queryBuilder.build(), Goods.class);
        Aggregations aggregations = resut.getAggregations();
        for(SpecParamDTO specParamDTO:specParamDTOS){
            String name = specParamDTO.getName();
            StringTerms terms =aggregations.get(name);
            List<String> paramValues=terms.getBuckets()
                    .stream()
                    .map(StringTerms.Bucket::getKeyAsString)
                    .collect(Collectors.toList());

            filterMap.put(name,paramValues);
        }
    }
}
